home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC Media 22
/
PC MEDIA CD22.iso
/
share
/
prog
/
datalib2
/
record.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1995-08-14
|
15KB
|
674 lines
#include "datapriv.hpp"
int recstrcmp(void *,void *);
/********************* Accessors ********************************/
char *record::indname()
{
if (oi) return oi->name;
return 0;
}
/********************* RECORD FUNCTIONS *************************/
// This is the constructor for a record, which grabs the space
// for it
/********************* CONSTRUCTOR *****************************/
record::record(database &dbp,char *indname)
{
if (!(recbuf=new char[dbp.reclen+1]) ||
!(recbufo=new char[dbp.reclen+1]) ||
!(fieldwork=new char[dbp.maxfieldl+1]))
dbfer(NORECSP);
if (dbp.isvalid()) {dbfer(INVDB); return;}
memset(recbuf,' ',dbp.reclen+1); // Clear record
memset(recbufo,' ',dbp.reclen+1);
rbp=recbuf;
db=&dbp;
rn=-1; // Indicate no record has been read into recbuf
delstate=NOTDEL;
ltype=-1; // Flag no last key found
curind=0;
nextind=0;
next=dbp.firstrec; // Link into record tree
dbp.firstrec=this;
prev=0;
// Now check for index, if found then copy in top cluster
oi=0;
if (oi=dbp.findex)
{
if (indname) oi=dbp.getindex(indname);
if (oi)
{
nextind=oi->firstrec; // Link into index tree
oi->firstrec=this;
curind=new indpt(oi->topind);
}
}
}
/********************** DESTRUCTOR *****************************/
// This is the destructor for a record, which also cleans up the pointers
record::~record(void)
{
if (oi)
{
record *rp,*fp;
fp=rp=oi->firstrec;
while(rp)
{
if (rp==this)
{
if (fp==rp) oi->firstrec=nextind;
else {while(fp->nextind!=rp) fp=fp->nextind; fp->nextind=nextind;}
break;
}
rp=rp->nextind;
}
}
killind();
if (recbuf) delete recbuf;
if (recbufo) delete recbufo;
if (fieldwork) delete fieldwork;
if (prev) prev->next=next; else db->firstrec=next;
if (next) next->prev=prev;
}
// Kill all indclus parents of the record
void record::killind(void)
{
indpt *pt,*npt;
if (curind) // Delete all references to current index
{
pt=curind;
do
{
npt=pt->parent; delete pt; pt=npt;
}
while (pt);
}
}
/********************* Record Assignment ***************/
void record::operator=(record &s)
{
indpt *pt,*npt;
if (curind)
{
killind();
curind=new indpt(s.curind->icp); curind->currec=s.curind->currec;
pt=(s.curind)->parent;
npt=curind;
while (pt)
{
npt->parent=new indpt(pt->icp); npt->currec=pt->currec;
npt=npt->parent; pt=pt->parent;
}
}
delstate=s.delstate;
rn=s.rn;
rbp=recbuf;
indflg=s.indflg;
oi=s.oi;
memmove(lkey,s.lkey,128); ltype=s.ltype; lseltype=s.lseltype;
memmove(recbuf,s.recbuf,db->reclen+1);
memmove(recbufo,s.recbuf,db->reclen+1);
memmove(fieldwork,s.fieldwork,db->maxfieldl+1);
}
/********************* SET the delete state ********************/
void record::setdelstate(int sval)
{
if (sval!=DEL && sval!=NOTDEL) return;
delstate=sval;
*recbuf=(sval==DEL) ? '*' : ' ';
}
/********************* SELECT record n from dbf ****************/
int record::seldbf(long n)
{
return select(n,ALL,1);
}
/********************* SELECT a record *************************/
// Select a record from the data file
int record::select(long n,int type,int dbfrec)
{
long ln=n; // Next Record
long fp;
if (n>0) // Select by record number
{
int rv;
if (type==ALL && (!curind || dbfrec)) // Fast select direct from dbf
{
if (n<1 || n>db->nrec) return CANTSEL;
rn=n;
fp=db->recstart+db->reclen*(rn-1); Fseek(db->dbfp,fp,SEEK_SET);
Fread(recbuf,db->reclen,1,db->dbfp);
memmove(recbufo,recbuf,db->reclen+1);
}
else
{
rv=select(FIRST,type); if (rv) return rv;
for(long i=1; i<n; i++) {rv=select(NEXT,type); if (rv) return rv;}
}
}
else // Select FIRST, LAST, NEXT or PREVIOUS
do
{
if (curind)
{
long tn;
if (!(tn=getind(ln))) return CANTSEL;
rn=tn;
}
else
{
if ((rn<=1 && ln==PREVIOUS) || (rn>=db->nrec && ln==NEXT)) return CANTSEL;
switch(ln)
{
case FIRST : rn=1; break;
case LAST : rn=db->nrec; break;
case NEXT : rn++; break;
case PREVIOUS : rn--; break;
}
}
fp=db->recstart+db->reclen*(rn-1);
Fseek(db->dbfp,fp,SEEK_SET);
Fread(recbuf,db->reclen,1,db->dbfp); // Read the record
memmove(recbufo,recbuf,db->reclen+1);
if (n==FIRST) ln=NEXT; // If deleted, which record next
if (n==LAST) ln=PREVIOUS;
}
while((type==DEL && *rbp==' ') || (type==NOTDEL && *rbp=='*'));
delstate=(*rbp=='*') ? DEL : NOTDEL;
return 0;
}
/********************* RECORD::SELECT - By Values ************/
// This is for finding records where the expression is true
int record::select(char *expr,long n,int type)
{
long ln=n;
int i;
int rv,rt;
char ws[128];
if (n>0)
{
rv=select(expr,FIRST,type); if (rv) return rv;
for(i=1; i<n; i++)
{
if (rv=select("",NEXT,type)) return rv;
}
return(0);
}
if (rv=select(n,type)) return rv; // Position at first possible record
if (n==FIRST) ln=NEXT;
if (n==LAST) ln=PREVIOUS;
if (*expr) eval(expr,ws,rv); // Tokenise expression if not already
while(1)
{
rv=eval(ws,rt);
if (!rv || rt!=OPLOG) return CANTSEL;
if (*ws=='T') return(0);
if (rv=select(ln,type)) return(rv);
}
}
// This is for numbers, call with record::compnum
// which compares two numbers
int record::select(int fieldnum,int value,long n,int type)
{
return(select(fieldnum,&value,compnum,n,type));
}
int compnum(void *field,void *num)
{
return(atoi((char *)field)!=*(int *)num);
}
// First function is string, simply call user defined select with
// the strcmp function
int record::select(int fieldnum,char *value,long n,int type)
{
return(select(fieldnum,value,recstrcmp,n,type));
}
int recstrcmp(void *s1,void *s2)
{
return(strcmp(trim((char *)s1),trim((char *)s2)));
}
// This is the user defined select function which allows the
// user to define a function to compare a field with a value,
// and then selects those record for which the function returns
// 0
int record::select(int fieldnum,void *value,
int (*cmp)(void *,void *),long n,int type)
{
long ln=n;
field *fp;
int i;
int rv;
if (n>0)
{
select(fieldnum,value,cmp,FIRST,type);
for(i=1; i<n; i++)
{
if (rv=select(fieldnum,value,cmp,NEXT,type)) return rv;
}
return(0);
}
if (!(fp=db->getfield(fieldnum))) return CANTSEL;
if (rv=select(n,type)) return rv; // Position at first possible record
if (n==FIRST) ln=NEXT;
if (n==LAST) ln=PREVIOUS;
while(1)
{
strncpy(fieldwork,rbp+fp->recpos,fp->len);
*(fieldwork+fp->len)=0;
if (!(*cmp)(fieldwork,value)) return(0);
if (rv=select(ln,type)) return(rv);
}
}
/********************* Field Operations on the record *********/
// This is overloaded, the 1st function returns by field number
// Get field by field number
char *record::getfield(int n,int trimflag)
{
field *fld;
if (n<1 || n>db->nfield || rn<0)
{
memset(fieldwork,' ',db->maxfieldl); fieldwork[db->maxfieldl]=0;
}
else
{
fld=db->getfield(n);
strncpy(fieldwork,rbp+fld->recpos,fld->len);
*(fieldwork+fld->len)=0;
if (fld->gettype()=='M') // Memo Field...
{
if (*fieldwork=='~') // New memo written
{
char *rv=*(char **)(rbp+fld->recpos+1);
return rv;
}
long bn=atol(fieldwork);
char *memow=db->memow; // Local copies of db variables
int memowl=db->memowl;
FILE *dbtp=db->dbtp;
if (bn)
{
char *ep; // end of memo
char *cbp=memow; // Current block pointer
int ef=0; // Flag end of memo found
if (!memow) memow=new char[512];
Fseek(dbtp,bn*512L,SEEK_SET);
for(int i=0; i<memowl; i++) // Read blocks until end found
{
Fread(cbp,512,1,dbtp);
if (ep=strstr(memow,"\x1a\x1a")) {*ep=0; ef=1; break;}
cbp+=512;
}
if (feof(dbtp) && !ef) {dbfer(INVMEMO); return ("");} // Invalid memo
if (!ef) // Need to find new memo length
{
memowl=0;
Fseek(dbtp,bn*512L,SEEK_SET);
do
{
memowl++;
Fread(memow,1,512,dbtp);
if (ep=strstr(memow,"\x1a\x1a")) {*ep=0; ef=1;}
}
while(!ef && !feof(dbtp));
if (memowl>1) {free(memow); memow=new char[memowl*512];}
if (!memow) {dbfer(NOMEMSP); return("");}
db->memow=memow;
db->memowl=memowl;
return(getfield(n,trimflag));
}
}
else {*fieldwork=0; return fieldwork;}
if (trimflag) return(rtrim(memow)); return(memow);
}
}
if (trimflag) return(rtrim(fieldwork));
return(fieldwork);
}
// Get field by field name
char *record::getfield(char *name,int trimflag)
{
field *fld;
long grn=0;
if (fld=db->getfield(name)) grn=fld->getnumber();
return(getfield(grn,trimflag));
}
/******************** Set Field Operations *********************/
int record::doset(char *value,field *fld)
{
char *sp=value;
char *fp=rbp+fld->recpos;
for(int i=0; i<fld->len; i++) *fp++=(*sp) ? *sp++ : ' ';
return 0;
}
int record::setfield(int num,char *value)
{
field *fld;
int i,obp;
char *fp;
if (!(fld=db->getfield(num))) return RECNOTSET;
switch(fld->gettype())
{
case 'C' : return doset(value,fld);
case 'D' : if (strlen(value)!=8) return RECNOTSET;
for(i=0; i<strlen(value); i++)
if (!isdigit(value[i])) return RECNOTSET;
return doset(value,fld);
case 'L' : strupr(value);
if (*value=='T' || *value=='F' ||
*value=='Y' || *value=='N') return doset(value,fld);
case 'M' : fp=rbp+fld->recpos; *fp='~'; *(char **)(fp+1)=value; return 0;
default : return RECNOTSET;
}
}
int record::setfield(int num,double value)
{
field *fld=db->getfield(num);
if (!fld || fld->gettype()!='N') return RECNOTSET;
char ws[128],ss[128];
int i,nex,wslen;
sprintf(ss,"%%.%df",fld->getrdp()); // May need %f here ?
wslen=sprintf(ws,ss,value);
nex=fld->getlen()-wslen;
if (nex>0) {memmove(ws+nex,ws,wslen+1); for(i=0; i<nex; i++) ws[i]=' ';}
return doset(ws,fld);
}
int record::setfield(char *name,double value)
{
field *fld=db->getfield(name);
if (!fld) return RECNOTSET;
return setfield(fld->getnumber(),value);
}
int record::setfield(char *name,char *value)
{
field *fld=db->getfield(name);
if (!fld) return RECNOTSET;
return setfield(fld->getnumber(),value);
}
/******************** Write Record Operations ******************/
int record::write(int type)
{
index *ip;
char res[128],reso[128]; // index results
int dum;
int nb; // No. of bytes written to file
long fp; // Position in file to write record
record *upp; // Pointer to records using index
long urn; // record no. of " " "
int e=1; // error number
int ret; // Return value
if (rn<1 && type==OVER) return NOWRUNR;
if (db->dbtp) wmemo(type); // Check if any memos need writing
if (type==OVER) // Overwrite, set position in dbf file
{
fp=db->recstart+db->reclen*(rn-1);
Fseek(db->dbfp,fp,SEEK_SET);
nb=Fwrite(recbuf,1,db->reclen,db->dbfp);
ip=db->findex;
record *dbr=db->firstrec;
while(dbr)
{
if (dbr->rn==rn && dbr!=this)
{
memmove(dbr->recbufo,recbuf,db->reclen+1);
memmove(dbr->recbuf,recbuf,db->reclen+1);
dbr->delstate=delstate;
}
dbr=dbr->next;
}
while(ip)
{
memmove(db->exwork,ip->tindexp,512);
rbp=recbufo;
eval(reso,dum);
rbp=recbuf;
eval(res,dum);
if (memcmp(res,reso,ip->topind->explen))
{
delkey(reso,ip);
inskey(res,ip);
upp=ip->firstrec; // Update record keys
while(upp)
{
if (upp->rn>=0)
{
urn=upp->rn;
upp->eval(res,dum);
if (upp->selkey(res,READIND,ip->indtype)) goto error;
while(upp->rn!=urn) if (upp->selkey()) {e=2; goto error;}
}
upp=upp->nextind;
}
}
ip=ip->next;
}
}
else // Add a new record at the file end
{
rn=++(db->nrec);
ip=db->findex;
while(ip)
{
memmove(db->exwork,ip->tindexp,512); // copy over index epression
eval(res,dum);
inskey(res,ip);
upp=ip->firstrec; // Update record keys
while(upp)
{
if (upp->rn>=0)
{
urn=upp->rn;
upp->eval(res,dum);
if (upp->selkey(res,READIND,ip->indtype)) {e=3; goto error;}
while(upp->rn!=urn) if (upp->selkey()) {e=4; goto error;}
}
upp=upp->nextind;
}
ip=ip->next;
}
fp=db->recstart+db->reclen*(rn-1);
Fseek(db->dbfp,fp,SEEK_SET);
nb=Fwrite(recbuf,1,db->reclen,db->dbfp);
}
memmove(recbufo,recbuf,db->reclen+1);
ret=(nb==db->reclen) ? 0 : WRFAIL;
db->change=1;
return ret;
error : // Fatal error in index write !
dbfer(ERINDW);
// printf("\nError code %d in index jig, rn %d\n",e,urn);
return WRFAIL;
}
// This routine checks to see if the record has any memos to write
void record::wmemo(int type)
{
char *ws=new char[512];
for(int i=1; i<=db->nfield; i++)
{
field *fld=db->getfield(i);
if (fld->gettype()=='M') // Memo field
{
char *mp=recbuf+fld->recpos;
if (*mp=='~') // A new memo field has been written
{
long bp=0; // New buffer pointer
unsigned int len; // Length of new memo
char *memo=*(char **)(mp+1);
if (len=strlen(memo))
{
if (len<=510) bp=atol(recbufo+fld->recpos); // Fit in the old block ?
if (type==NEW || !bp) // Must create a new block
{
Fseek(db->dbtp,0,SEEK_SET); Fread(ws,1,512,db->dbtp);
bp=*(long *)ws;
*(long *)ws=bp+len/512+1;
Fseek(db->dbtp,0,SEEK_SET); Fwrite(ws,1,512,db->dbtp);
}
Fseek(db->dbtp,(long)(bp*512L),SEEK_SET);
Fwrite(memo,1,len,db->dbtp);
fputc(0x1a,db->dbtp); fputc(0x1a,db->dbtp); len+=2;
memset(ws,' ',512);
if (len & 0x1ff) Fwrite(ws,1,(512-(len & 0x1ff)),db->dbtp);
sprintf(ws,"%010ld",bp);
memcpy(mp,ws,10);
}
else memset(mp,'0',10);
}
}
}
delete ws;
}
/****************************************************************
EVAL routines
*****************************************************************/
int record::eval(char *expr,void *result,int &rtype)
{
char *wsp;
if (!db->ex)
{
db->ex=new expval(db);
db->exwork=new char[512];
}
if (!db->ex || !db->exwork || db->ex->erflag==2) return 0;
db->ex->erflag=0;
wsp=db->exwork;
db->ex->exetoken(expr,&wsp);
if (db->ex->erflag==1) {db->ex->erflag=3; return 0;} // show tokeniser error
return eval(result,rtype);
}
int record::eval(void *result,int &rtype)
{
op *rv;
char *wsp;
if (!db->ex || db->ex->erflag>1) return 0; // Fatal or tokeniser error
wsp=db->exwork;
indflg=0;
rv=db->ex->exeval(&wsp,this);
if (db->ex->erflag) return 0;
rtype=rv->optype;
if (rtype==OPINT) *(double *)result=rv->num;
if (rtype==OPSTR) strcpy((char *)result,rv->str);
if (rtype==OPDATE) strcpy((char *)result,rv->date);
if (rtype==OPLOG)
{*(char *)result=(rv->num) ? 'T' : 'F'; ((char *)result)[1]=0;}
return 1;
}